﻿using System;
using System.Collections.Generic;
using AdaptiveAgents.Distributions;
using AdaptiveAgents.Agents;
using AdaptiveAgents.Loggers;
using AdaptiveAgents.Games;
using System.Data;

namespace AdaptiveAgents.Experiments
{
    /// <summary>
    /// </summary>
    class GreedyNAgentsExperiment : Experiment
    {
        public int NumberOfAgents { get; private set; }
        public double[] EpsilonData { get; private set; }
        public double[] CompetenceData { get; private set; }

        /// <summary>
        /// Constructor - 
        /// </summary>
        public GreedyNAgentsExperiment(int numberOfAgents, double[] epsilonData, double[] competenceData)
            : base(50, numberOfAgents, 1) 
        {
            NumberOfAgents = numberOfAgents;
            EpsilonData  = epsilonData;
            CompetenceData = competenceData;

            if (EpsilonData == null || CompetenceData == null)
            { 
                throw new Exception("Epsilon or Compentence is null"); 
            }

            if (NumberOfAgents != EpsilonData.Length && NumberOfAgents != CompetenceData.Length)
            {
                throw new Exception("Epsilon or Compentence size does not match Number of agents");
            }
        }

        /// <summary>
        /// run the experiment
        /// </summary>
        public override void runExperiment()
        {
            int GAMES = 1; //number of games to run
            int ROUNDS = 500; //number of round each game

            if (EpsilonData == null || CompetenceData == null)
            {
                throw new Exception("Epsilon or Compentence is null");
            }

            if (NumberOfAgents != EpsilonData.Length && NumberOfAgents != CompetenceData.Length)
            {
                throw new Exception("Epsilon or Compentence size does not match Number of agents");
            }

            NAgentsDataTable dt = new NAgentsDataTable(NumberOfAgents, ROUNDS);

            DataRow dr = dt.NewRow();

            //run games
            for (int i = 0; i < GAMES; ++i)
            {
                //write to console to see progress
                if (i % 100 == 0)
                    System.Console.WriteLine(i);

                ///first - create the agents data so all the games will run with the same data
                ///second - update the data into the agents
                ///third - run the games

                //set new rows for the game
                dr = dt.NewRow();
                

                //enter game number
                dr["GameNum"] = i;

                for (int j=0; j<NumberOfAgents; j++)
                {
                    dr["Epsilon" + j] = EpsilonData[j];
                }

                for (int j=0; j<NumberOfAgents; j++)
                {
                    dr["Competence" + j] = CompetenceData[j];
                }   

                //*/*/*/*/*/*/* Run The Experiments *\*\*\*\*\*\*\\

                ////Greedy

                // first let's set the environment
                Environment environment = new Environment();

                //Create a new population of players
                //The agentType is greedy so we create a greedy agent and two normal agents
                List<Agent> agents = generatePlayers(environment, _logger);

                for (int j = 0; j < NumberOfAgents; j++)
                {
                    Agent agent = agents[j];
                    environment.addAgent(agent);
                }

                //create a game
                NormalGame game = (NormalGame)GameFactory.create(Games.GameType.Normal, environment, ROUNDS);

                double utility = 0;

                //set the agents with their Competence

                for (int j = 0; j < environment.agents.Count; j++)
                {
                    Agent ag = environment.agents[j];
                    ag.Competence = new Blocks(CompetenceData[j]);
                }

                //enter to the game the row it needs to write the game data to
                //game.Dr1 = dr;

                //run the game and get back the utility of the game
                utility = game.start(_logger);

                //update utility
                dr["utility Greedy"] = utility;

                //add the game data to the DataTable
                dt.Rows.Add(dr);
            }

            //create file names and write to the files
            String stamp = DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss");
            dt.WriteXml("GreedyExperiment_" + NumberOfAgents + "_" +  stamp + ".xml");
        }

        /// <summary>
        /// Generate the agents for the game
        /// </summary>
        /// <param name="env">the EnvironmentType of the game</param>
        /// <param name="epsilon0">epsilon for agent 0</param>
        /// <param name="epsilon1">epsilon for agent 1</param>
        /// <param name="epsilon2">epsilon for agent 2</param>
        /// <param name="logger">Logger for logging the data</param>
        /// <returns>a list of agents for the game</returns>
        protected List<Agent> generatePlayers(Environment env, Logger logger)
        {
            List<Agent> retList = new List<Agent>(NumberOfAgents);

            //create director
            AgentDirector director = new AgentDirector();

            Agent ag;
            director.Builder = new Agents.GreedyBuilder();

            //construct the first agent according to the director
            director.construct(0, env, EpsilonData[0], logger);
            ag = director.getAgent();
            ag.ChangeStrategyPoint = 1; //todo: change it ???
            retList.Add(ag);

            for (int i = 1; i < NumberOfAgents; i++)
            {
                //construct the 2nd and 3rd agents as normal agents
                director.Builder = new Agents.NormalBuilder();
                director.construct(i, env, EpsilonData[i], logger);
                retList.Add(director.getAgent());
            }

            return retList;
        }
    }


    class NAgentsDataTable : DataTable
    {
        public NAgentsDataTable(int numberOfAgents, int rounds): base("Greedy" + numberOfAgents)
        {
            DataColumn cln = new DataColumn("GameNum", typeof(double));
            Columns.Add(cln);

            for (int i = 0; i < numberOfAgents; i++)
            {
                cln = new DataColumn("Competence" + i, typeof(double));
                Columns.Add(cln);
            }

            for (int i = 0; i < numberOfAgents; i++)
            {
                cln = new DataColumn("Epsilon" + i, typeof(double));
                Columns.Add(cln);
            }

            cln = new DataColumn("utility Greedy", typeof(double));
            Columns.Add(cln);

            //add rows for each round
            //for (int i = 0; i < rounds; ++i)
            //{
            //    cln = new DataColumn(i.ToString(), typeof(double));
            //    Columns.Add("utility " + cln);
            //}
        }
    }
}
